/**JavaFile******************************************************************

  FileName    [Program that implements the Museum Problem]

  Synopsis [ Museum Problem template ]

  Author      [Iria Estevez-Ayres <ayres@it.uc3m.es>]

  Copyright   [Copyright (c) 2019 Carlos III University of Madrid
  All rights reserved.

  Permission is hereby granted, without written agreement and without license
  or royalty fees, to use, copy, modify, and distribute this software and its
  documentation for any purpose, provided that the above copyright notice and
  the following two paragraphs appear in all copies of this software.

  IN NO EVENT SHALL THE CARLOS III UNIVERSITY OF MADRID BE LIABLE TO ANY PARTY
  FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
  OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE CARLOS III
  UNIVERSITY OF MADRID HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

  THE CARLOS III UNIVERSITY OF MADRID SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN
  "AS IS" BASIS, AND CARLOS III UNIVERSITY OF MADRID HAS NO OBLIGATION TO
  PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.]

 ******************************************************************************/


import java.lang.*;
import java.util.*;
import java.util.concurrent.Semaphore;

class Person extends Thread{
    private int me;
    private Semaphore s45Sem, s20aSem,  s40Sem, s20bSem, s30Sem;
    private static int numIn45, numIn20a, numIn40, numIn20b, numIn30;


    private Semaphore mutex45, mutex20a, mutex40, mutex20b, mutex30;
    private Random rnd = new Random();

    Person(int me, Semaphore s45Sem, Semaphore s20aSem, Semaphore s40Sem,
            Semaphore s20bSem, Semaphore s30Sem, Semaphore mutex45,
            Semaphore mutex20a, Semaphore mutex40, Semaphore mutex20b,
            Semaphore mutex30){
        this.s45Sem = s45Sem;
        this.s20aSem = s20aSem;
        this.s40Sem = s40Sem;
        this.s20bSem = s20bSem;
        this.s30Sem = s30Sem;
        this.mutex45 = mutex45;
        this.mutex20a = mutex20a;
        this.mutex40 = mutex40;
        this.mutex20b = mutex20b;
        this.mutex30 = mutex30;
        this.me = me;
    }

    private void handleAcquire(Semaphore s)
    {
        try {
            s.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void handleJoin()
    {
        try {
            this.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    private void handleSleep(long millis)
    {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    private void room45in(int time){
        handleAcquire(mutex45);
        numIn45++;
        mutex45.release();
        String cardinal = "st";

        if (time == 2){
            handleAcquire(mutex20a);
            numIn20a--;
            mutex20a.release();
            //Out of room 20a
            s20aSem.release();
            cardinal = "nd";
        }

        handleAcquire(s45Sem);
        System.out.println("[ "+ me +" ] I am inside room 45 for the "+ time + cardinal + " time. There are " + numIn45  + " people");
    }


    private void room20ain(){
        handleAcquire(mutex20a);
        numIn20a++;
        mutex20a.release();

        handleAcquire(mutex45);
        numIn45--;
        mutex45.release();
        //Out of room 45
        s45Sem.release();

        handleAcquire(s20aSem);
        System.out.println("[ "+ me +" ] I am inside room 20a. there are "
                + numIn20a + " people");
    }


    private void room40in(int time){
        handleAcquire(mutex40);
        numIn40++;
        mutex40.release();
        String cardinal = "st";

        if (time == 1){
            handleAcquire(mutex45);
            numIn45--;
            mutex45.release();
            //Out of room 45
            s45Sem.release();
        }

        if (time == 2){
            handleAcquire(mutex20b);
            numIn20b--;
            mutex20b.release();
            //Out of room20b
            s20bSem.release();
            cardinal = "nd";
        }

        handleAcquire(s40Sem);
        System.out.println("[ "+ me +" ] I am inside room 40 for the" + time + cardinal + " time. there are "
                + numIn40 + " people");
    }


    private void room20bin(){
        handleAcquire(mutex20b);
        numIn20b++;
        mutex20b.release();
        handleAcquire(mutex40);
        numIn40--;
        mutex40.release();
        //Out of room 40
        s40Sem.release();

        handleAcquire(s20bSem);
        System.out.println("[ "+ me +" ] I am inside room 20b. there are " + numIn20b + " people");
    }

    private void room30in(){
        handleAcquire(mutex30);
        numIn30++;
        mutex30.release();
        handleAcquire(mutex40);
        numIn40--;
        mutex40.release();
        //Out of room 40
        s40Sem.release();

        handleAcquire(s30Sem);
        System.out.println("[ "+ me +" ] I am inside room 30. there are "
                + numIn30 + " people");
    }

    private void room30out(){
        handleAcquire(mutex30);
        numIn30--;
        mutex30.release();
        s30Sem.release();
    }

    public void run(){
        handleSleep(rnd.nextInt(6000));
        System.out.println("[ "+ me +" ] Trying to enter");

        room45in(1);
        handleSleep(1000);

        room20ain();
        handleSleep(1000);

        room45in(2);
        handleSleep(1000);

        room40in(1);
        handleSleep(1000);

        room20bin();
        handleSleep(1000);

        room40in(2);
        handleSleep(1000);

        room30in();
        handleSleep(rnd.nextInt(1000));

        room30out();
        System.out.println("[ "+ me +" ] Outside the museum");
    }
}

public class Museum{

    static final int NUM_PEOPLE = 200;

    public static void main(String[] args){
        Person p;
        Semaphore s45Sem, s20aSem, s40Sem, s20bSem, s30Sem;
        Semaphore mutex45, mutex20a, mutex40, mutex20b, mutex30;

        s45Sem = new Semaphore(45);
        s20aSem = new Semaphore(20);
        s40Sem = new Semaphore(40);
        s20bSem = new Semaphore(20);
        s30Sem = new Semaphore(30);

        mutex45 = new Semaphore(1);
        mutex20a = new Semaphore(1);
        mutex40 = new Semaphore(1);
        mutex20b = new Semaphore(1);
        mutex30 = new Semaphore(1);

        ArrayList<Person> people = new ArrayList<Person>(NUM_PEOPLE);

        for (int j = 1; j <= NUM_PEOPLE; ++j)
        {
            p = new Person(j, s45Sem, s20aSem, s40Sem, s20bSem, s30Sem, mutex45, mutex20a, mutex40, mutex20b, mutex30);
            people.add(p);
        }

        for (Iterator it = people.listIterator(); it.hasNext();)
        {
            p = (Person) it.next();
            p.start();
        }

        for (Iterator it = people.listIterator(); it.hasNext();)
        {
            p = (Person) it.next();
            p.handleJoin();
        }
    }
}
